{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "15_Computer_Vision",
      "version": "0.3.2",
      "provenance": [],
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "metadata": {
        "id": "bOChJSNXtC9g",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "# Computer Vision"
      ]
    },
    {
      "metadata": {
        "id": "OLIxEDq6VhvZ",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "<img src=\"https://raw.githubusercontent.com/GokuMohandas/practicalAI/master/images/logo.png\" width=150>\n",
        "\n",
        "In this notebook we're going to cover the basics of computer vision using CNNs. So far we've explored using CNNs for text but their initial origin began with computer vision tasks.\n",
        "\n",
        "\n"
      ]
    },
    {
      "metadata": {
        "id": "wKX2R_FT4hSQ",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "<img src=\"https://raw.githubusercontent.com/GokuMohandas/practicalAI/master/images/cnn_cv.png\" width=650>"
      ]
    },
    {
      "metadata": {
        "id": "zOUWqHjL6hmU",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "# Set up"
      ]
    },
    {
      "metadata": {
        "id": "kjXAaAyx6i5W",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "!pip3 install torch torchvision\n",
        "!pip install Pillow==4.0.0\n",
        "!pip install PIL\n",
        "!pip install image"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "vXjCadon6toa",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import os\n",
        "from argparse import Namespace\n",
        "import collections\n",
        "import json\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from PIL import Image\n",
        "import re\n",
        "import torch"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "N518ySE16trp",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Set Numpy and PyTorch seeds\n",
        "def set_seeds(seed, cuda):\n",
        "    np.random.seed(seed)\n",
        "    torch.manual_seed(seed)\n",
        "    if cuda:\n",
        "        torch.cuda.manual_seed_all(seed)\n",
        "        \n",
        "# Creating directories\n",
        "def create_dirs(dirpath):\n",
        "    if not os.path.exists(dirpath):\n",
        "        os.makedirs(dirpath)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "KG_bcOZ58vhB",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "# Data"
      ]
    },
    {
      "metadata": {
        "id": "PGQLzyss8wja",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "We're going to first get some data. A popular computer vision classification dataset is [CIFAR10](https://www.cs.toronto.edu/~kriz/cifar.html) which contains images from ten unique classes."
      ]
    },
    {
      "metadata": {
        "id": "NYy0WlkB9AoK",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Don't worry we aren't using tensorflow, just using it to get some data\n",
        "import tensorflow as tf\n",
        "import matplotlib.pyplot as plt"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "Ka-WxeEJ8vAd",
        "colab_type": "code",
        "outputId": "ec24e935-0562-4c55-c2ab-2bfe59a49128",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "cell_type": "code",
      "source": [
        "# Load data and combine\n",
        "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()\n",
        "X = np.vstack([x_train, x_test])\n",
        "y = np.vstack([y_train, y_test]).squeeze(1)\n",
        "print (\"x:\", X.shape)\n",
        "print (\"y:\", y.shape)"
      ],
      "execution_count": 8,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz\n",
            "170500096/170498071 [==============================] - 50s 0us/step\n",
            "x: (60000, 32, 32, 3)\n",
            "y: (60000,)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "0YIiwLWcBH07",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "Each image has length 32, width 32 and three color channels (RGB). We are going to save these images in a directory. Each image will have it's own directory (name will be the class)."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "xWqzC-M1NCzx",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "!rm -rf cifar10_data"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "AdZjOciC-Bzm",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Classes\n",
        "classes = {0: 'plane', 1: 'car', 2: 'bird', 3: 'cat', 4: 'deer', 5: 'dog', \n",
        "           6: 'frog', 7: 'horse', 8: 'ship', 9: 'truck'}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "DbNtoIxD8dxc",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Create image directories\n",
        "data_dir = \"cifar10_data\"\n",
        "os.mkdir(data_dir)\n",
        "for _class in classes.values():\n",
        "    os.mkdir(os.path.join(data_dir, _class))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "wf5EY4Ey8kFq",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Save images for each class\n",
        "for i, (image, label) in enumerate(zip(X, y)):\n",
        "    _class = classes[label]\n",
        "    im = Image.fromarray(image)\n",
        "    im.save(os.path.join(data_dir, _class, \"{0:02d}.png\".format(i)))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "PrD2oUFu_KVF",
        "colab_type": "code",
        "outputId": "e336f8c8-5cf5-4235-ec10-68761b6c725c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 101
        }
      },
      "cell_type": "code",
      "source": [
        "# Visualize some samples\n",
        "num_samples = len(classes)\n",
        "for i, _class in enumerate(classes.values()):  \n",
        "    for file in os.listdir(os.path.join(data_dir, _class)):\n",
        "        if file.endswith(\".png\"):\n",
        "            plt.subplot(1, num_samples, i+1)\n",
        "            plt.title(\"{0}\".format(_class))\n",
        "            img = Image.open(os.path.join(data_dir, _class, file))\n",
        "            plt.imshow(img)\n",
        "            plt.axis(\"off\")\n",
        "            break"
      ],
      "execution_count": 14,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAd8AAABUCAYAAADDAD33AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzsvXm4XVV9//9aa+29z3zumHtv5puE\nTAQpiFgHMCQINwFRDGgtBae29qnap0i1aMXqY621Po/a1mprbX9Pa60jUpywSouggiAQ5iGEhISb\n5CY3ufOZ9rDW+v2x9jlJlNxL/cKNbc/7efLknnP22eez1/6sz/z5bGGttbTRRhtttNFGG/MGebIJ\naKONNtpoo43/a2gr3zbaaKONNtqYZ7SVbxtttNFGG23MM9rKt4022mijjTbmGW3l20YbbbTRRhvz\njLbybaONNtpoo415xq+c8n3ve9/LZz/72ZNNxv963HXXXVxwwQW/8P4nPvEJvvzlLz+rc1xwwQXc\nddddzzVpzzmiKOLGG288qTTcc889bN68+aTS8P+C/wn0v/vd72bjxo38+Mc/PtmknBD79u3j1FNP\nPdlkPOeY7bq++MUv8ld/9VfzTNFRfO1rX3tOzvNc3zvvOTtTG/8r8Ed/9Ecnm4TnHI8++ig33ngj\nl1566ckmpY3nEd/97nf5/ve/z7Jly042KW0cgyuvvPKk/bbWmo9//OO8/vWvP2k0nAgnzfO96667\nuOSSS/jYxz7G0NAQmzdv5v777z/umPvuu49t27axZcsWLrroIu644w7AWSDnnHMOX/jCF7jkkks4\n99xzuemmmwCw1vK3f/u3DA0NsWnTJj7ykY+gtZ6Xa7rxxhsZGhpiaGiI97znPURRxNe//nW2bt3K\nhRdeyG/91m+xf/9+AG644Qbe+c538qY3vYmPf/zj80LfM+Ev//IvGRoaYsuWLWzfvv24yMPmzZtb\na3ngwAEefvhhLr74YoaGhvjoRz960miGZ7/WR44c4Z3vfCf3338/V1xxxbzS+NnPfpaNGzdy6aWX\ntng3iiI+8pGPtHj+7//+71vHP/nkk1x55ZUMDQ1xySWX8NBDDwFur7zhDW/gD//wD+fVOHom+sMw\n5E//9E8ZGhpi69atfOxjH2vtrx//+Mds3LiRrVu38tWvfpUXvvCF7Nu3b15oveqqqzDG8Nu//dtc\nfvnlfOpTn2Lr1q1s376dyclJ/vAP/5ChoSEuuugi/uEf/qH1vRtuuIGXv/zlvPrVr+aGG25g7dq1\n80Lv9ddfzyWXXMLGjRv5zne+gzGGT33qU2zZsoUtW7bw3ve+l1qt1rq2Y6/nZz/7Ga997Wu56KKL\n2Lp1K9/73vcAmJ6e5j3veQ9DQ0Ocf/75fOMb33heaE+ShPe///0MDQ1xwQUX8M53vpNKpfKM1wXw\n6U9/mve///2Akymf//zn2bZtGy95yUued4/4LW95CzMzM2zZsoVNmzYdt45XXXUV3/zmN1vHHvv6\nRz/6UUvW/d7v/R6Tk5O/cO53v/vd/Nmf/dkvT5w9Sbjzzjvt+vXr7Xe/+11rrbVf+9rX7Gte8xp7\n7bXX2s985jPWWmtf9apX2e985zvWWmv//d//3b7yla+01lo7PDxsTz31VPuv//qv1lprb7rpJnvB\nBRe0jrv44ovt9PS0jePYvu1tb2sd93xieHjYvuQlL7EHDx60xhj7jne8w37uc5+zp512mh0ZGbHW\nWvve977X/smf/Im11tpvfOMb9owzzrBPPfXU807bM6G5/s31/epXv/oL679p0yZ73XXXtb5z2WWX\n2a985SvWWrfm69ats3feeee80/7LrPWb3vSmeaVx586d9uyzz7aHDx+2SZLYt7/97XbTpk32b//2\nb+2b3vQmG4ahrVar9tJLL7W33HKL1VrbCy+80H7ta1+z1lp7zz332HPOOcfGcWzvvPNO+4IXvMDe\ncccdJ53+z33uc/Z3f/d3bRzHtl6v28suu8zeeOONNkkS+7KXvczeeuut1lprP/axj9l169bZ4eHh\neaN5zZo1dmRkxF555ZX2rW99q9VaW2ut/cAHPmA/8IEPWGutnZiYsOedd569++677cTEhD399NPt\njh07rNbavutd77Jr1qx5XmkcHh62a9eutV/60pestdZ+73vfs+eff779zne+Yy+99FJbrVZtkiT2\n93//91v78OevZ9u2bfauu+6y1lr71FNP2WuuucZaa+373vc++8d//MdWa23Hxsbsxo0b7Y4dO57z\na/jhD39o3/jGN1pjjDXG2E996lP2y1/+8jNel7XW/s3f/E1rL27atMm+/e1vt0mS2CNHjtizzz7b\nPvbYY885jU0MDw/b9evXW2t/cR2vvPJKe+ONN7aObb6uVqv2xS9+cWvtPvKRj9gPfehDx52ruQ+S\n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\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7ff5ea0e6a20>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "metadata": {
        "id": "IgvzGk0S4wPY",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "# Classification"
      ]
    },
    {
      "metadata": {
        "id": "gVOkpS6O5b-B",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "Our task will be to classify the class given the image. We're going to architect a basic CNN to process the input images and produce a classification."
      ]
    },
    {
      "metadata": {
        "id": "Y5C78l1j5UTm",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Arguments"
      ]
    },
    {
      "metadata": {
        "id": "yLW2_1CG2Eyg",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "With image data, we won't be save our split data files. We will only read from the image directory."
      ]
    },
    {
      "metadata": {
        "id": "RTMvq5A849-w",
        "colab_type": "code",
        "outputId": "f9142a73-fbf6-4504-f064-70417c6f8e6d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "cell_type": "code",
      "source": [
        "args = Namespace(\n",
        "    seed=1234,\n",
        "    cuda=True,\n",
        "    shuffle=True,\n",
        "    data_dir=\"cifar10_data\",\n",
        "    vectorizer_file=\"vectorizer.json\",\n",
        "    model_state_file=\"model.pth\",\n",
        "    save_dir=\"cifar10_model\",\n",
        "    train_size=0.7,\n",
        "    val_size=0.15,\n",
        "    test_size=0.15,\n",
        "    num_epochs=10,\n",
        "    early_stopping_criteria=5,\n",
        "    learning_rate=1e-3,\n",
        "    batch_size=128,\n",
        "    num_filters=100,\n",
        "    hidden_dim=100,\n",
        "    dropout_p=0.1,\n",
        ")\n",
        "\n",
        "# Set seeds\n",
        "set_seeds(seed=args.seed, cuda=args.cuda)\n",
        "\n",
        "# Create save dir\n",
        "create_dirs(args.save_dir)\n",
        "\n",
        "# Expand filepaths\n",
        "args.vectorizer_file = os.path.join(args.save_dir, args.vectorizer_file)\n",
        "args.model_state_file = os.path.join(args.save_dir, args.model_state_file)\n",
        "\n",
        "# Check CUDA\n",
        "if not torch.cuda.is_available():\n",
        "    args.cuda = False\n",
        "args.device = torch.device(\"cuda\" if args.cuda else \"cpu\")\n",
        "print(\"Using CUDA: {}\".format(args.cuda))"
      ],
      "execution_count": 17,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Using CUDA: True\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "xaYCCEHOrpGB",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Data"
      ]
    },
    {
      "metadata": {
        "id": "8iF6nxgDtOWk",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Convert image file to NumPy array\n",
        "def img_to_array(fp):\n",
        "    img = Image.open(fp)\n",
        "    array = np.asarray(img, dtype=\"float32\")\n",
        "    return array"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "3VlHdV9r5VzN",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Load data\n",
        "data = []\n",
        "for i, _class in enumerate(classes.values()):  \n",
        "    for file in os.listdir(os.path.join(data_dir, _class)):\n",
        "        if file.endswith(\".png\"):\n",
        "            full_filepath = os.path.join(data_dir, _class, file)\n",
        "            data.append({\"image\": img_to_array(full_filepath), \"category\": _class})"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "WvknlOjM5V1z",
        "colab_type": "code",
        "outputId": "69e2f3bf-42df-4b08-b086-fc744e263db8",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 221
        }
      },
      "cell_type": "code",
      "source": [
        "# Convert to Pandas DataFrame\n",
        "df = pd.DataFrame(data)\n",
        "print (\"Image shape:\", df.image[0].shape)\n",
        "df.head()"
      ],
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Image shape: (32, 32, 3)\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>category</th>\n",
              "      <th>image</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>plane</td>\n",
              "      <td>[[[160.0, 173.0, 167.0], [151.0, 164.0, 155.0]...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>plane</td>\n",
              "      <td>[[[190.0, 228.0, 243.0], [188.0, 223.0, 238.0]...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>plane</td>\n",
              "      <td>[[[255.0, 255.0, 255.0], [253.0, 254.0, 251.0]...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>plane</td>\n",
              "      <td>[[[193.0, 216.0, 227.0], [191.0, 213.0, 225.0]...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>plane</td>\n",
              "      <td>[[[234.0, 234.0, 234.0], [231.0, 231.0, 231.0]...</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "  category                                              image\n",
              "0    plane  [[[160.0, 173.0, 167.0], [151.0, 164.0, 155.0]...\n",
              "1    plane  [[[190.0, 228.0, 243.0], [188.0, 223.0, 238.0]...\n",
              "2    plane  [[[255.0, 255.0, 255.0], [253.0, 254.0, 251.0]...\n",
              "3    plane  [[[193.0, 216.0, 227.0], [191.0, 213.0, 225.0]...\n",
              "4    plane  [[[234.0, 234.0, 234.0], [231.0, 231.0, 231.0]..."
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 20
        }
      ]
    },
    {
      "metadata": {
        "id": "GXtRpahp5V6p",
        "colab_type": "code",
        "outputId": "6ea08026-d651-4a75-d40f-94086fb211ce",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 187
        }
      },
      "cell_type": "code",
      "source": [
        "by_category = collections.defaultdict(list)\n",
        "for _, row in df.iterrows():\n",
        "    by_category[row.category].append(row.to_dict())\n",
        "for category in by_category:\n",
        "    print (\"{0}: {1}\".format(category, len(by_category[category])))"
      ],
      "execution_count": 21,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "plane: 6000\n",
            "car: 6000\n",
            "bird: 6000\n",
            "cat: 6000\n",
            "deer: 6000\n",
            "dog: 6000\n",
            "frog: 6000\n",
            "horse: 6000\n",
            "ship: 6000\n",
            "truck: 6000\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "AYVNBhLgt-38",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "final_list = []\n",
        "for _, item_list in sorted(by_category.items()):\n",
        "    if args.shuffle:\n",
        "        np.random.shuffle(item_list)\n",
        "    n = len(item_list)\n",
        "    n_train = int(args.train_size*n)\n",
        "    n_val = int(args.val_size*n)\n",
        "    n_test = int(args.test_size*n)\n",
        "\n",
        "  # Give data point a split attribute\n",
        "    for item in item_list[:n_train]:\n",
        "        item['split'] = 'train'\n",
        "    for item in item_list[n_train:n_train+n_val]:\n",
        "        item['split'] = 'val'\n",
        "    for item in item_list[n_train+n_val:]:\n",
        "        item['split'] = 'test'  \n",
        "\n",
        "    # Add to final list\n",
        "    final_list.extend(item_list)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "o8GNPotNt-6X",
        "colab_type": "code",
        "outputId": "162a2ddb-db83-4708-b48d-ecbf1d61dd4f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "cell_type": "code",
      "source": [
        "split_df = pd.DataFrame(final_list)\n",
        "split_df[\"split\"].value_counts()"
      ],
      "execution_count": 23,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "train    42000\n",
              "test      9000\n",
              "val       9000\n",
              "Name: split, dtype: int64"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 23
        }
      ]
    },
    {
      "metadata": {
        "id": "cLdJQPBmX0yJ",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Vocabulary"
      ]
    },
    {
      "metadata": {
        "id": "EB-kpxhct-_S",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class Vocabulary(object):\n",
        "    def __init__(self, token_to_idx=None):\n",
        "\n",
        "        # Token to index\n",
        "        if token_to_idx is None:\n",
        "            token_to_idx = {}\n",
        "        self.token_to_idx = token_to_idx\n",
        "\n",
        "        # Index to token\n",
        "        self.idx_to_token = {idx: token \\\n",
        "                             for token, idx in self.token_to_idx.items()}\n",
        "\n",
        "    def to_serializable(self):\n",
        "        return {'token_to_idx': self.token_to_idx}\n",
        "\n",
        "    @classmethod\n",
        "    def from_serializable(cls, contents):\n",
        "        return cls(**contents)\n",
        "\n",
        "    def add_token(self, token):\n",
        "        if token in self.token_to_idx:\n",
        "            index = self.token_to_idx[token]\n",
        "        else:\n",
        "            index = len(self.token_to_idx)\n",
        "            self.token_to_idx[token] = index\n",
        "            self.idx_to_token[index] = token\n",
        "        return index\n",
        "\n",
        "    def add_tokens(self, tokens):\n",
        "        return [self.add_token[token] for token in tokens]\n",
        "\n",
        "    def lookup_token(self, token):\n",
        "        return self.token_to_idx[token]\n",
        "\n",
        "    def lookup_index(self, index):\n",
        "        if index not in self.idx_to_token:\n",
        "            raise KeyError(\"the index (%d) is not in the Vocabulary\" % index)\n",
        "        return self.idx_to_token[index]\n",
        "\n",
        "    def __str__(self):\n",
        "        return \"<Vocabulary(size=%d)>\" % len(self)\n",
        "\n",
        "    def __len__(self):\n",
        "        return len(self.token_to_idx)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "QcpS2G28t_Bv",
        "colab_type": "code",
        "outputId": "d0f38e9b-311a-42e1-a00a-ca75c8cf672e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "cell_type": "code",
      "source": [
        "# Vocabulary instance\n",
        "category_vocab = Vocabulary()\n",
        "for index, row in df.iterrows():\n",
        "    category_vocab.add_token(row.category)\n",
        "print (category_vocab) # __str__\n",
        "print (len(category_vocab)) # __len__\n",
        "index = category_vocab.lookup_token(\"dog\")\n",
        "print (index)\n",
        "print (category_vocab.lookup_index(index))"
      ],
      "execution_count": 25,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "<Vocabulary(size=10)>\n",
            "10\n",
            "5\n",
            "dog\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "ubECmrcqZIHI",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Sequence vocbulary"
      ]
    },
    {
      "metadata": {
        "id": "37pGFTBiZIbm",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "from collections import Counter\n",
        "import string"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "YvWL2JcgZPaw",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class SequenceVocabulary():\n",
        "    def __init__(self, train_means, train_stds):\n",
        "        \n",
        "        self.train_means = train_means\n",
        "        self.train_stds = train_stds\n",
        "        \n",
        "    def to_serializable(self):\n",
        "        contents = {'train_means': self.train_means,\n",
        "                    'train_stds': self.train_stds}\n",
        "        return contents\n",
        "    \n",
        "    @classmethod\n",
        "    def from_dataframe(cls, df):\n",
        "        train_data = df[df.split == \"train\"]\n",
        "        means = {0:[], 1:[], 2:[]}\n",
        "        stds = {0:[], 1:[], 2:[]}\n",
        "        for image in train_data.image:\n",
        "            for dim in range(3):\n",
        "                means[dim].append(np.mean(image[:, :, dim]))\n",
        "                stds[dim].append(np.std(image[:, :, dim]))\n",
        "        train_means = np.array((np.mean(means[0]), np.mean(means[1]), \n",
        "                                np.mean(means[2])), dtype=\"float64\").tolist()\n",
        "        train_stds = np.array((np.mean(stds[0]), np.mean(stds[1]), \n",
        "                               np.mean(stds[2])), dtype=\"float64\").tolist()\n",
        "            \n",
        "        return cls(train_means, train_stds)\n",
        "        \n",
        "    def __str__(self):\n",
        "        return \"<SequenceVocabulary(train_means: {0}, train_stds: {1}>\".format(\n",
        "            self.train_means, self.train_stds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "-ODlh2wcahqH",
        "colab_type": "code",
        "outputId": "b165d02a-3b92-40b8-92f5-5055533e6447",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 54
        }
      },
      "cell_type": "code",
      "source": [
        "# Create SequenceVocabulary instance\n",
        "image_vocab = SequenceVocabulary.from_dataframe(split_df)\n",
        "print (image_vocab) # __str__"
      ],
      "execution_count": 28,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "<SequenceVocabulary(train_means: [125.44353485107422, 123.07293701171875, 113.99832153320312], train_stds: [51.60729217529297, 50.876190185546875, 51.25827407836914]>\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "lUZKa0c9YD0V",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Vectorizer"
      ]
    },
    {
      "metadata": {
        "id": "RyxHZLTFX5VC",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class ImageVectorizer(object):\n",
        "    def __init__(self, image_vocab, category_vocab):\n",
        "        self.image_vocab = image_vocab\n",
        "        self.category_vocab = category_vocab\n",
        "\n",
        "    def vectorize(self, image):\n",
        "        \n",
        "        # Avoid modifying the actual df\n",
        "        image = np.copy(image)\n",
        "        \n",
        "        # Normalize\n",
        "        for dim in range(3):\n",
        "            mean = self.image_vocab.train_means[dim]\n",
        "            std = self.image_vocab.train_stds[dim]\n",
        "            image[:, :, dim] = ((image[:, :, dim] - mean) / std)\n",
        "            \n",
        "        # Reshape frok (32, 32, 3) to (3, 32, 32)\n",
        "        image = np.swapaxes(image, 0, 2)\n",
        "        image = np.swapaxes(image, 1, 2)\n",
        "                \n",
        "        return image\n",
        "    \n",
        "    @classmethod\n",
        "    def from_dataframe(cls, df):\n",
        "        \n",
        "        # Create class vocab\n",
        "        category_vocab = Vocabulary()   \n",
        "        for category in sorted(set(df.category)):\n",
        "            category_vocab.add_token(category)\n",
        "            \n",
        "        # Create image vocab\n",
        "        image_vocab = SequenceVocabulary.from_dataframe(df)\n",
        "        \n",
        "        return cls(image_vocab, category_vocab)\n",
        "\n",
        "    @classmethod\n",
        "    def from_serializable(cls, contents):\n",
        "        image_vocab = SequenceVocabulary.from_serializable(contents['image_vocab'])\n",
        "        category_vocab = Vocabulary.from_serializable(contents['category_vocab'])\n",
        "        return cls(image_vocab=image_vocab, \n",
        "                   category_vocab=category_vocab)\n",
        "    \n",
        "    def to_serializable(self):\n",
        "        return {'image_vocab': self.image_vocab.to_serializable(),\n",
        "                'category_vocab': self.category_vocab.to_serializable()}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "yXWIhtFUiDUe",
        "colab_type": "code",
        "outputId": "62f2c017-da89-4333-8cd3-f84abe05723b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 88
        }
      },
      "cell_type": "code",
      "source": [
        "# Vectorizer instance\n",
        "vectorizer = ImageVectorizer.from_dataframe(split_df)\n",
        "print (vectorizer.image_vocab)\n",
        "print (vectorizer.category_vocab)\n",
        "image_vector = vectorizer.vectorize(split_df.iloc[0].image)\n",
        "print (image_vector.shape)"
      ],
      "execution_count": 30,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "<SequenceVocabulary(train_means: [125.44353485107422, 123.07293701171875, 113.99832153320312], train_stds: [51.60729217529297, 50.876190185546875, 51.25827407836914]>\n",
            "<Vocabulary(size=10)>\n",
            "(3, 32, 32)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "Xm7s9RPThF3c",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Dataset"
      ]
    },
    {
      "metadata": {
        "id": "2mL4eEdNX5c1",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "from torch.utils.data import Dataset, DataLoader"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "Dzegh16nX5fY",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class ImageDataset(Dataset):\n",
        "    def __init__(self, df, vectorizer):\n",
        "        self.df = df\n",
        "        self.vectorizer = vectorizer\n",
        "\n",
        "        # Data splits\n",
        "        self.train_df = self.df[self.df.split=='train']\n",
        "        self.train_size = len(self.train_df)\n",
        "        self.val_df = self.df[self.df.split=='val']\n",
        "        self.val_size = len(self.val_df)\n",
        "        self.test_df = self.df[self.df.split=='test']\n",
        "        self.test_size = len(self.test_df)\n",
        "        self.lookup_dict = {'train': (self.train_df, self.train_size), \n",
        "                            'val': (self.val_df, self.val_size),\n",
        "                            'test': (self.test_df, self.test_size)}\n",
        "        self.set_split('train')\n",
        "\n",
        "        # Class weights (for imbalances)\n",
        "        class_counts = df.category.value_counts().to_dict()\n",
        "        def sort_key(item):\n",
        "            return self.vectorizer.category_vocab.lookup_token(item[0])\n",
        "        sorted_counts = sorted(class_counts.items(), key=sort_key)\n",
        "        frequencies = [count for _, count in sorted_counts]\n",
        "        self.class_weights = 1.0 / torch.tensor(frequencies, dtype=torch.float32)\n",
        "\n",
        "    @classmethod\n",
        "    def load_dataset_and_make_vectorizer(cls, df):\n",
        "        train_df = df[df.split=='train']\n",
        "        return cls(df, ImageVectorizer.from_dataframe(train_df))\n",
        "\n",
        "    @classmethod\n",
        "    def load_dataset_and_load_vectorizer(cls, df, vectorizer_filepath):\n",
        "        vectorizer = cls.load_vectorizer_only(vectorizer_filepath)\n",
        "        return cls(df, vectorizer)\n",
        "\n",
        "    def load_vectorizer_only(vectorizer_filepath):\n",
        "        with open(vectorizer_filepath) as fp:\n",
        "            return ImageVectorizer.from_serializable(json.load(fp))\n",
        "\n",
        "    def save_vectorizer(self, vectorizer_filepath):\n",
        "        with open(vectorizer_filepath, \"w\") as fp:\n",
        "            json.dump(self.vectorizer.to_serializable(), fp)\n",
        "\n",
        "    def set_split(self, split=\"train\"):\n",
        "        self.target_split = split\n",
        "        self.target_df, self.target_size = self.lookup_dict[split]\n",
        "\n",
        "    def __str__(self):\n",
        "        return \"<Dataset(split={0}, size={1})\".format(\n",
        "            self.target_split, self.target_size)\n",
        "\n",
        "    def __len__(self):\n",
        "        return self.target_size\n",
        "\n",
        "    def __getitem__(self, index):\n",
        "        row = self.target_df.iloc[index]\n",
        "        image_vector = self.vectorizer.vectorize(row.image)\n",
        "        category_index = self.vectorizer.category_vocab.lookup_token(row.category)\n",
        "        return {'image': image_vector, \n",
        "                'category': category_index}\n",
        "\n",
        "    def get_num_batches(self, batch_size):\n",
        "        return len(self) // batch_size\n",
        "\n",
        "    def generate_batches(self, batch_size, shuffle=True, drop_last=True, device=\"cpu\"):\n",
        "        dataloader = DataLoader(dataset=self, batch_size=batch_size, \n",
        "                                shuffle=shuffle, drop_last=drop_last)\n",
        "        for data_dict in dataloader:\n",
        "            out_data_dict = {}\n",
        "            for name, tensor in data_dict.items():\n",
        "                out_data_dict[name] = data_dict[name].to(device)\n",
        "            yield out_data_dict"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "-sW6otUGX5iA",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 102
        },
        "outputId": "5abb9822-37dd-4680-ba93-f85f79c77296"
      },
      "cell_type": "code",
      "source": [
        "# Dataset instance\n",
        "dataset = ImageDataset.load_dataset_and_make_vectorizer(split_df)\n",
        "print (dataset) # __str__\n",
        "input_ = dataset[10] # __getitem__\n",
        "print (input_['image'].shape)\n",
        "category = input_['category']\n",
        "print (dataset.vectorizer.category_vocab.lookup_index(category))\n",
        "print (dataset.class_weights)"
      ],
      "execution_count": 33,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "<Dataset(split=train, size=42000)\n",
            "(3, 32, 32)\n",
            "bird\n",
            "tensor([0.0002, 0.0002, 0.0002, 0.0002, 0.0002, 0.0002, 0.0002, 0.0002, 0.0002,\n",
            "        0.0002])\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "SjPHp36i3G_i",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Model"
      ]
    },
    {
      "metadata": {
        "id": "bPaf6Dy2X5ko",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import torch.nn as nn\n",
        "import torch.nn.functional as F"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "RKRPzX1nX5nN",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class ImageModel(nn.Module):\n",
        "    def __init__(self, num_hidden_units, num_classes, dropout_p):\n",
        "        super(ImageModel, self).__init__()\n",
        "        self.conv1 = nn.Conv2d(3, 10, kernel_size=5) # input_channels:3 , output_channels:10 (aka num filters)\n",
        "        self.conv2 = nn.Conv2d(10, 20, kernel_size=5) \n",
        "        self.conv_dropout = nn.Dropout2d(dropout_p)\n",
        "        self.fc1 = nn.Linear(20*5*5, num_hidden_units)\n",
        "        self.dropout = nn.Dropout(dropout_p)\n",
        "        self.fc2 = nn.Linear(num_hidden_units, num_classes)\n",
        "\n",
        "    def forward(self, x, apply_softmax=False):\n",
        "          \n",
        "        # Conv pool\n",
        "        z = self.conv1(x) # (N, 10, 28, 28)\n",
        "        z = F.max_pool2d(z, 2) # (N, 10, 14, 14)\n",
        "        z = F.relu(z)\n",
        "        \n",
        "        # Conv pool\n",
        "        z = self.conv2(z) # (N, 20, 10, 10)\n",
        "        z = self.conv_dropout(z) \n",
        "        z = F.max_pool2d(z, 2) # (N, 20, 5, 5)\n",
        "        z = F.relu(z)\n",
        "        \n",
        "        # Flatten\n",
        "        z = z.view(-1, 20*5*5)\n",
        "        \n",
        "        # FC\n",
        "        z = F.relu(self.fc1(z))\n",
        "        z = self.dropout(z)\n",
        "        y_pred = self.fc2(z)\n",
        "        \n",
        "        if apply_softmax:\n",
        "            y_pred = F.softmax(y_pred, dim=1)\n",
        "        return y_pred \n",
        "        "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "jAiIbY9TBGef",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "### Training"
      ]
    },
    {
      "metadata": {
        "id": "bnMvjt9JX5p4",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import torch.optim as optim"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "o0bR1rzqX5sg",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class Trainer(object):\n",
        "    def __init__(self, dataset, model, model_state_file, save_dir, device, \n",
        "                 shuffle, num_epochs, batch_size, learning_rate, \n",
        "                 early_stopping_criteria):\n",
        "        self.dataset = dataset\n",
        "        self.class_weights = dataset.class_weights.to(device)\n",
        "        self.device = device\n",
        "        self.model = model.to(device)\n",
        "        self.save_dir = save_dir\n",
        "        self.device = device\n",
        "        self.shuffle = shuffle\n",
        "        self.num_epochs = num_epochs\n",
        "        self.batch_size = batch_size\n",
        "        self.loss_func = nn.CrossEntropyLoss(self.class_weights)\n",
        "        self.optimizer = optim.Adam(self.model.parameters(), lr=learning_rate)\n",
        "        self.scheduler = optim.lr_scheduler.ReduceLROnPlateau(\n",
        "            optimizer=self.optimizer, mode='min', factor=0.5, patience=1)\n",
        "        self.train_state = {\n",
        "            'stop_early': False, \n",
        "            'early_stopping_step': 0,\n",
        "            'early_stopping_best_val': 1e8,\n",
        "            'early_stopping_criteria': early_stopping_criteria,\n",
        "            'learning_rate': learning_rate,\n",
        "            'epoch_index': 0,\n",
        "            'train_loss': [],\n",
        "            'train_acc': [],\n",
        "            'val_loss': [],\n",
        "            'val_acc': [],\n",
        "            'test_loss': -1,\n",
        "            'test_acc': -1,\n",
        "            'model_filename': model_state_file}\n",
        "    \n",
        "    def update_train_state(self):\n",
        "\n",
        "        # Verbose\n",
        "        print (\"[EPOCH]: {0:02d} | [LR]: {1} | [TRAIN LOSS]: {2:.2f} | [TRAIN ACC]: {3:.1f}% | [VAL LOSS]: {4:.2f} | [VAL ACC]: {5:.1f}%\".format(\n",
        "          self.train_state['epoch_index'], self.train_state['learning_rate'], \n",
        "            self.train_state['train_loss'][-1], self.train_state['train_acc'][-1], \n",
        "            self.train_state['val_loss'][-1], self.train_state['val_acc'][-1]))\n",
        "\n",
        "        # Save one model at least\n",
        "        if self.train_state['epoch_index'] == 0:\n",
        "            torch.save(self.model.state_dict(), self.train_state['model_filename'])\n",
        "            self.train_state['stop_early'] = False\n",
        "\n",
        "        # Save model if performance improved\n",
        "        elif self.train_state['epoch_index'] >= 1:\n",
        "            loss_tm1, loss_t = self.train_state['val_loss'][-2:]\n",
        "\n",
        "            # If loss worsened\n",
        "            if loss_t >= self.train_state['early_stopping_best_val']:\n",
        "                # Update step\n",
        "                self.train_state['early_stopping_step'] += 1\n",
        "\n",
        "            # Loss decreased\n",
        "            else:\n",
        "                # Save the best model\n",
        "                if loss_t < self.train_state['early_stopping_best_val']:\n",
        "                    torch.save(self.model.state_dict(), self.train_state['model_filename'])\n",
        "\n",
        "                # Reset early stopping step\n",
        "                self.train_state['early_stopping_step'] = 0\n",
        "\n",
        "            # Stop early ?\n",
        "            self.train_state['stop_early'] = self.train_state['early_stopping_step'] \\\n",
        "              >= self.train_state['early_stopping_criteria']\n",
        "        return self.train_state\n",
        "  \n",
        "    def compute_accuracy(self, y_pred, y_target):\n",
        "        _, y_pred_indices = y_pred.max(dim=1)\n",
        "        n_correct = torch.eq(y_pred_indices, y_target).sum().item()\n",
        "        return n_correct / len(y_pred_indices) * 100\n",
        "  \n",
        "    def run_train_loop(self):\n",
        "        for epoch_index in range(self.num_epochs):\n",
        "            self.train_state['epoch_index'] = epoch_index\n",
        "      \n",
        "            # Iterate over train dataset\n",
        "\n",
        "            # initialize batch generator, set loss and acc to 0, set train mode on\n",
        "            self.dataset.set_split('train')\n",
        "            batch_generator = self.dataset.generate_batches(\n",
        "                batch_size=self.batch_size, shuffle=self.shuffle, \n",
        "                device=self.device)\n",
        "            running_loss = 0.0\n",
        "            running_acc = 0.0\n",
        "            self.model.train()\n",
        "\n",
        "            for batch_index, batch_dict in enumerate(batch_generator):\n",
        "                # zero the gradients\n",
        "                self.optimizer.zero_grad()\n",
        "                \n",
        "                # compute the output\n",
        "                y_pred = self.model(x=batch_dict['image'])\n",
        "                \n",
        "                # compute the loss\n",
        "                loss = self.loss_func(y_pred, batch_dict['category'])\n",
        "                loss_t = loss.item()\n",
        "                running_loss += (loss_t - running_loss) / (batch_index + 1)\n",
        "\n",
        "                # compute gradients using loss\n",
        "                loss.backward()\n",
        "\n",
        "                # use optimizer to take a gradient step\n",
        "                self.optimizer.step()\n",
        "                \n",
        "                # compute the accuracy\n",
        "                acc_t = self.compute_accuracy(y_pred, batch_dict['category'])\n",
        "                running_acc += (acc_t - running_acc) / (batch_index + 1)\n",
        "\n",
        "            self.train_state['train_loss'].append(running_loss)\n",
        "            self.train_state['train_acc'].append(running_acc)\n",
        "\n",
        "            # Iterate over val dataset\n",
        "\n",
        "            # initialize batch generator, set loss and acc to 0, set eval mode on\n",
        "            self.dataset.set_split('val')\n",
        "            batch_generator = self.dataset.generate_batches(\n",
        "                batch_size=self.batch_size, shuffle=self.shuffle, device=self.device)\n",
        "            running_loss = 0.\n",
        "            running_acc = 0.\n",
        "            self.model.eval()\n",
        "\n",
        "            for batch_index, batch_dict in enumerate(batch_generator):\n",
        "\n",
        "                # compute the output\n",
        "                y_pred = self.model(x=batch_dict['image'])\n",
        "\n",
        "                # compute the loss\n",
        "                loss = self.loss_func(y_pred, batch_dict['category'])\n",
        "                loss_t = loss.to(\"cpu\").item()\n",
        "                running_loss += (loss_t - running_loss) / (batch_index + 1)\n",
        "\n",
        "                # compute the accuracy\n",
        "                acc_t = self.compute_accuracy(y_pred, batch_dict['category'])\n",
        "                running_acc += (acc_t - running_acc) / (batch_index + 1)\n",
        "\n",
        "            self.train_state['val_loss'].append(running_loss)\n",
        "            self.train_state['val_acc'].append(running_acc)\n",
        "\n",
        "            self.train_state = self.update_train_state()\n",
        "            self.scheduler.step(self.train_state['val_loss'][-1])\n",
        "            if self.train_state['stop_early']:\n",
        "                break\n",
        "          \n",
        "    def run_test_loop(self):\n",
        "        # initialize batch generator, set loss and acc to 0, set eval mode on\n",
        "        self.dataset.set_split('test')\n",
        "        batch_generator = self.dataset.generate_batches(\n",
        "            batch_size=self.batch_size, shuffle=self.shuffle, device=self.device)\n",
        "        running_loss = 0.0\n",
        "        running_acc = 0.0\n",
        "        self.model.eval()\n",
        "\n",
        "        for batch_index, batch_dict in enumerate(batch_generator):\n",
        "            # compute the output\n",
        "            y_pred = self.model(x=batch_dict['image'])\n",
        "\n",
        "            # compute the loss\n",
        "            loss = self.loss_func(y_pred, batch_dict['category'])\n",
        "            loss_t = loss.item()\n",
        "            running_loss += (loss_t - running_loss) / (batch_index + 1)\n",
        "\n",
        "            # compute the accuracy\n",
        "            acc_t = self.compute_accuracy(y_pred, batch_dict['category'])\n",
        "            running_acc += (acc_t - running_acc) / (batch_index + 1)\n",
        "\n",
        "        self.train_state['test_loss'] = running_loss\n",
        "        self.train_state['test_acc'] = running_acc\n",
        "    \n",
        "    def plot_performance(self):\n",
        "        # Figure size\n",
        "        plt.figure(figsize=(15,5))\n",
        "\n",
        "        # Plot Loss\n",
        "        plt.subplot(1, 2, 1)\n",
        "        plt.title(\"Loss\")\n",
        "        plt.plot(trainer.train_state[\"train_loss\"], label=\"train\")\n",
        "        plt.plot(trainer.train_state[\"val_loss\"], label=\"val\")\n",
        "        plt.legend(loc='upper right')\n",
        "\n",
        "        # Plot Accuracy\n",
        "        plt.subplot(1, 2, 2)\n",
        "        plt.title(\"Accuracy\")\n",
        "        plt.plot(trainer.train_state[\"train_acc\"], label=\"train\")\n",
        "        plt.plot(trainer.train_state[\"val_acc\"], label=\"val\")\n",
        "        plt.legend(loc='lower right')\n",
        "\n",
        "        # Save figure\n",
        "        plt.savefig(os.path.join(self.save_dir, \"performance.png\"))\n",
        "\n",
        "        # Show plots\n",
        "        plt.show()\n",
        "    \n",
        "    def save_train_state(self):\n",
        "        with open(os.path.join(self.save_dir, \"train_state.json\"), \"w\") as fp:\n",
        "            json.dump(self.train_state, fp)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "Ug60AELzX5vT",
        "colab_type": "code",
        "outputId": "74f5b9db-ebc0-47d5-e496-afabe9c0ca7b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 153
        }
      },
      "cell_type": "code",
      "source": [
        "# Initialization\n",
        "dataset = ImageDataset.load_dataset_and_make_vectorizer(split_df)\n",
        "dataset.save_vectorizer(args.vectorizer_file)\n",
        "vectorizer = dataset.vectorizer\n",
        "model = ImageModel(num_hidden_units=args.hidden_dim, \n",
        "                   num_classes=len(vectorizer.category_vocab),\n",
        "                   dropout_p=args.dropout_p)\n",
        "print (model.named_modules)"
      ],
      "execution_count": 38,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "<bound method Module.named_modules of ImageModel(\n",
            "  (conv1): Conv2d(3, 10, kernel_size=(5, 5), stride=(1, 1))\n",
            "  (conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))\n",
            "  (conv_dropout): Dropout2d(p=0.1)\n",
            "  (fc1): Linear(in_features=500, out_features=100, bias=True)\n",
            "  (dropout): Dropout(p=0.1)\n",
            "  (fc2): Linear(in_features=100, out_features=10, bias=True)\n",
            ")>\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "vF9kAEXEX5a4",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Train\n",
        "trainer = Trainer(dataset=dataset, model=model, \n",
        "                  model_state_file=args.model_state_file, \n",
        "                  save_dir=args.save_dir, device=args.device,\n",
        "                  shuffle=args.shuffle, num_epochs=args.num_epochs, \n",
        "                  batch_size=args.batch_size, learning_rate=args.learning_rate, \n",
        "                  early_stopping_criteria=args.early_stopping_criteria)\n",
        "trainer.run_train_loop()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "2G6I5YWtt_Ea",
        "colab_type": "code",
        "outputId": "0ca459c6-0053-4a43-82a3-6a1e1c6e5c33",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 335
        }
      },
      "cell_type": "code",
      "source": [
        "# Plot performance\n",
        "trainer.plot_performance()"
      ],
      "execution_count": 40,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAA2sAAAE+CAYAAAATaYj9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3Xd81eXd//HXGTk52TshG0IGeysg\nyAgiIFjFiW3dtaVWrdparXfVtra3+mvt3Sra3uJdrbNWRMSJQgEB2XuHsDLJ3vOs3x+BKJWV5MDJ\nOXk/H494cr7nfK/zuRJJzjvX+BpcLpcLERERERER6VaMni5AREREREREvk1hTUREREREpBtSWBMR\nEREREemGFNZERERERES6IYU1ERERERGRbkhhTUREREREpBtSWBPppKysLI4dO+bpMkRERC6IOXPm\n8J3vfMfTZYj0KAprIiIiInJGOTk5hISEkJCQwNatWz1djkiPobAm4mYtLS08/vjjTJs2jRkzZvD0\n00/jcDgAeOONN5gxYwbTp0/nuuuu48CBA2c8LiIi0h28//77TJ8+nVmzZrFo0aL244sWLWLatGlM\nmzaNhx56iNbW1tMeX79+PVOnTm0/95v3n3/+eX71q19x3XXX8eqrr+J0OvnNb37DtGnTyM7O5qGH\nHsJmswFQWVnJ3LlzmTJlCldeeSWrV69mxYoVzJo166Sar7nmGpYuXXq+vzQi55XZ0wWI+Jp//OMf\nHDt2jI8//hi73c73v/99PvroI6ZMmcJf/vIXli9fTnBwMJ9++ikrVqwgPj7+lMczMjI83RUREREc\nDgdffPEFP/nJTzCZTDz77LO0trZSWlrKM888w6JFi4iNjeXee+/ltddeY/r06ac8Pnjw4DO+zsqV\nK/nggw+IjIxkyZIlbNq0iY8++gin08ns2bP55JNPuOqqq3j22Wfp27cvf/vb39izZw+33347q1at\noqysjH379tGvXz+KiorIy8tjwoQJF+irJHJ+KKyJuNmKFSu44447MJvNmM1mrrzyStasWcMVV1yB\nwWBgwYIFzJo1ixkzZgBgs9lOeVxERKQ7WL16NYMHDyY4OBiAiy++mOXLl1NdXc3w4cOJi4sD4Nln\nn8VkMvHee++d8vjmzZvP+DpDhw4lMjISgGnTpjF58mT8/PwAGDx4MPn5+UBbqJs/fz4AAwYMYNmy\nZVgsFqZNm8bHH39Mv379WLp0KVOmTMFisbj/CyJyAWkapIibVVZWEhYW1n4/LCyMiooK/Pz8ePXV\nV9myZQvTpk3ju9/9Lvv37z/tcRERke5g4cKFrFixglGjRjFq1Cg+//xz3n//faqqqggNDW1/nr+/\nP2az+bTHz+abvzsrKyt5+OGHmTZtGtOnT2fZsmW4XC4AqqurCQkJaX/uiRA5c+ZMPv74YwCWLl3K\nFVdc0bWOi3QDCmsibhYdHU11dXX7/erqaqKjo4G2vwA+99xzrF27lvHjx/PEE0+c8biIiIgn1dTU\nsGHDBtavX8+mTZvYtGkTGzduZOfOnRiNRqqqqtqfW19fT3l5OREREac8bjKZ2tdwA9TW1p72df/n\nf/4Hs9nMhx9+yGeffcbEiRPbHwsPDz+p/YKCAmw2GxdddBF2u53ly5dz4MABLrnkEnd9GUQ8RmFN\nxM0mTZrEggULcDgcNDY28sEHHzBx4kT279/PfffdR2trKxaLhUGDBmEwGE57XERExNM+/vhjxowZ\nc9J0QrPZzPjx42ltbWXLli0UFBTgcrl44oknWLBgARMnTjzl8ZiYGMrKyqioqMDhcPDhhx+e9nUr\nKirIzMzEYrGwb98+tm7dSmNjIwDZ2dm8//77AOTm5nLNNdfgcDgwGo1cccUVPPnkk2RnZ7dPoRTx\nZlqzJtIFN998MyaTqf3+7373O26++Wby8/OZOXMmBoOB6dOnt69DS0pKYtasWfj5+REUFMTjjz9O\nZmbmKY+LiIh42qJFi7j11lu/dXzq1Km8+OKL/Pa3v+XWW2/FZDIxePBgbr/9dvz9/U97/Nprr+Xq\nq68mISGBq666ir17957yde+44w4efvhhFi5cyKhRo3j44Yf5r//6L4YMGcJDDz3Eww8/THZ2NkFB\nQfzxj3/EarUCbVMhX3nlFU2BFJ9hcJ2YACwiIiIi4sXKy8uZPXs2K1asOOmPqSLeStMgRURERMQn\nPPfcc9x0000KauIzFNZERERExKuVl5czZcoUysvLueOOOzxdjojbaBqkiIiIiIhIN6SRNRERERER\nkW5IYU1ERERERKQbuuBb95eV1XW5jYiIQKqqGt1QjeepL92T+tI9qS/d05n6EhMTcoGr8W76HXky\n9aV78pW++Eo/QH3prtzx+9ErR9bMZt/Z4Ud96Z7Ul+5JfemefKkvvsCXvh/qS/fkK33xlX6A+tJd\nuaMvXhnWREREREREfJ3CmoiIiIiISDeksCYiIiIiItINKayJiIiIiIh0QwprIiIiIiIi3ZDCmoiI\niIiISDeksCYiIiIiItINKayJiPQgK1YsO6fn/eUvz1JUVHieqxEREZEzUVgTEekhiouLWLp0yTk9\n96c//RkJCYnnuSIRERE5E7OnC+iophY7H646xPC+kfj7+c4VzkVEzrc//ekZ9u7dzaWXXsTll8+g\nuLiIP//5RZ566reUlZXS1NTEHXf8kHHjLuWee37Igw/+guXLl9HQUE9e3lEKCwu4776fMXbsOE93\nRURE5IJwuVw0NNspq26irLqJ8ppm+qVEkJYQekFe3+vC2t6jVby0aCffvSyDy0Yle7ocERGvcdNN\nN7Nw4b/o06cveXlHePHFl6mqquTii8cwY8YsCgsLeOyxRxg37tKTzistLeGPf3yOdeu+4oMP3lNY\nExERn2J3OKmsbaasupnS46Hsmx9NLY6Tnj8yK4afzB58QWrzurCWFBMEQE5+tcKaiHitv3+4my+3\nFLi1zYv6xXJDdvo5Pbd//4EAhISEsnfvbhYvXojBYKS2tuZbzx0yZBgAsbGx1NfXu69gERGRC6Sh\n2UZZdROlVSdCWHN7GKuobcbl+vY5Fj8jMeEBxIQFtN2GW4kJDyAzOfyC1e11YS0mPIDIUCs5BTW4\nXC4MBoOnSxIR8Tp+fn4AfPHFZ9TW1vLCCy9TW1vLD35w87eeazJ9PeXcdarfZiIiIt3Q/rwqFqw8\nSHF5I40t9lM+JzzYQnpi2PEwFkBs+NfBLDTI4vGs4XVhzWAwMDAtilXbCimpaqJXZKCnSxIR6bA7\nrhzIlWNSLuhrGo1GHI6Tp3JUV1cTH5+A0Whk5cp/Y7PZLmhNIiIi7uZ0uvh47REWrT4MQHxUEBlJ\nxwNZREB7MIsOs3b7PTC8LqwBDOwTyaptheTkVyusiYico9TUPuzfv4/4+ATCw9umcEyalM0jjzzI\nnj27mDnzO8TGxvLKK/M9XKmIiEjn1NS38NKHe9h7tIrIUH9+9J2BZCRduGmL7uadYa1vNNC2bm3C\n0AQPVyMi4h0iIiJYuPDjk47Fxyfwj3/8s/3+5ZfPAOD22+8CIC3t6zVwaWnpzJv30gWoVEREpON2\nH6lk/uLd1DbaGJYezR0z+xMc4OfpsrrEK8NaSlwIQVYzOfnVni5FREREREQ8yOF08sHqw3z81VGM\n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\ncBXvv/8ub731+pDXpLAmIiK9/Cwm4iN8d8pkOTVXkD/hoYEjZlY4EZGhlJ4+jo8+2tB7+YUXXun9\n+YILume8v+SSy4e0JuOQPpqIiIiIiIicFoU1ERERERERH6SwJiIiIiIi4oMU1kRERERERHyQwpqI\niIiIiIgPUlgTERERERHxQQprIiIiIiIiPkhhTURERERExAcprImIiIiIiPgghTUREREREREfZPB4\nPB5vFyEiIiIiIiLH0pk1ERERERERH6SwJiIiIiIi4oMU1kRERERERHyQwpqIiIiIiIgPUlgTERER\nERHxQQprIiIiIiIiPsjs7QL68qtf/Yrt27djMBj48Y9/zIQJE3q3rV27lkceeQSTycTcuXO55ZZb\nvFhp337961+zZcsWOjo6+Pa3v82SJUt6ty1cuJDIyEhMJhMADz/8MBEREd4q9ZQ2bNjAHXfcQVpa\nGgCjRo3i3nvv7d0+nJ6Xf/7zn7z++uu9l3ft2sW2bdt6L48bN47Jkyf3Xn722Wd7nyNfsX//fm6+\n+Wauv/56li9fTklJCXfddRednZ2Eh4fz0EMP4efnd8x9TvV/5U0nass999xDR0cHZrOZhx56iPDw\n8N7b9/W36E3/3Za7776bzMxMgoODAbjhhhuYP3/+MfcZLs/L7bffTnV1NQA1NTVkZGTw85//vPf2\nr732Gr/97W+Jj48HYNasWXznO9/xSu0jmY6PvkfHRx0fB4uOj8PjeRmU46PHh23YsMHzrW99y+Px\neDzZ2dmeq6666pjtS5cu9Rw8eNDT2dnpueaaazxZWVneKPO0rFu3znPjjTd6PB6P59ChQ5558+Yd\ns33BggWehoYGL1R25tavX++57bbbTrp9OD0vR9uwYYPn/vvvP+a66dOne6ma09PY2OhZvny55//9\nv//nee655zwej8dz9913e95++22Px+Px/OY3v/G88MILx9ynr/8rbzlRW+666y7PW2+95fF4PJ7n\nn3/e8+CDDx5zn77+Fr3lRG350Y9+5Pnwww9Pep/h9Lwc7e677/Zs3779mOteffVVz//+7/8OVYnn\nJB0ffZOOj75Dx0cdHwfbUB0ffbob5Lp161i8eDEAKSkp1NbW0tDQAEBhYSFOp5OoqCiMRiPz5s1j\n3bp13iz3lKZNm8Zvf/tbAIKCgmhubqazs9PLVQ284fa8HO33v/89N998s7fLOCN+fn489dRTuN3u\n3us2bNjAokWLAFiwYMFxv/9T/V9504nact9993HRRRcBEBISQk1NjbfKOyMnaktfhtPzctiBAweo\nr6/3mU84zyU6Pg4/w+15OZqOj96l4+PweV4OG8jjo0+HtcrKSkJCQnovu1wuKioqAKioqMDlcp1w\nmy8ymUzYbDYAXnnlFebOnXtcd4H77ruPa665hocffhiPx+ONMk9bdnY2N910E9dccw2ffvpp7/XD\n7Xk5bMeOHURFRR3ThQCgra2NO++8k6uvvppnnnnGS9WdnNlsxt/f/5jrmpube7t1hIaGHvf7P9X/\nlTedqC02mw2TyURnZyd///vfueyyy46738n+Fr3pRG0BeP7557nuuuv43ve+x6FDh47ZNpyel8P+\n9re/sXz58hNu27hxIzfccANf+9rX2L1792CWeE7S8dF36fjoG3R81PFxsA3V8dHnx6wdzddfoE/H\n+++/zyuvvMJf/vKXY66//fbbmTNnDk6nk1tuuYWVK1fyuc99zktVnlpiYiK33norS5cupbCwkOuu\nu4533333uH7fw8krr7zC5z//+eOuv+uuu7j88ssxGAwsX76cqVOnct5553mhwv45nf8ZX/+/6uzs\n5K677mLGjBnMnDnzmG3D6W9x2bJlBAcHk56ezpNPPsnjjz/OT37yk5Pe3tefl7a2NrZs2cL9999/\n3LaJEyficrmYP38+27Zt40c/+hFvvPHG0Bd5DvH1v5fToeOjb9Lx0Xfp+OibBvr46NNn1txuN5WV\nlb2Xy8vLez/Z+e9tZWVlZ3RK1RvWrFnDE088wVNPPYXD4Thm2xVXXEFoaChms5m5c+eyf/9+L1XZ\nt4iICC6++GIMBgPx8fGEhYVRVlYGDM/nBbq7RkyaNOm466+55hoCAwOx2WzMmDHDp5+Xw2w2Gy0t\nLcCJf/+n+r/yRffccw8JCQnceuutx2071d+ir5k5cybp6elA94QJ//23NNyel02bNp20e0dKSkrv\n4PBJkyZx6NChEdmtzZt0fPRNOj76Nh0fdXwcCgN9fPTpsDZ79mxWrlwJQGZmJm63G7vdDkBsbCwN\nDQ0UFRXR0dHBqlWrmD17tjfLPaX6+np+/etf86c//al3tpujt91www20tbUB3U/y4dl7fNHrr7/O\n008/DXR366iqquqdmWu4PS/Q/YIdGBh43KdNBw4c4M4778Tj8dDR0cHWrVt9+nk5bNasWb3/N+++\n+y5z5sw5Zvup/q98zeuvv47FYuH2228/6faT/S36mttuu43CwkKg+83Pf/8tDafnBWDnzp2MGTPm\nhNueeuop3nzzTaB7piyXy+Vzs8QNdzo++iYdH32bjo86Pg6FgT4+Gjw+fi7x4YcfZvPmzRgMBu67\n7z52796Nw+HgwgsvZNOmTTz88MMALFmyhBtuuMHL1Z7cyy+/zGOPPUZSUlLvdeeffz6jR4/mwgsv\n5K9//SsrVqzAarUyduxY7r33XgwGgxcrPrmGhgZ+8IMfUFdXR3t7O7feeitVVVXD8nmB7umIH330\nUf785z8D8OSTTzJt2jQmTZrEQw89xPr16zEajSxcuNDnph/ftWsXDz74IMXFxZjNZiIiInj44Ye5\n++67aW1tJTo6mgceeACLxcL3vvc9HnjgAfz9/Y/7vzrZi4q321JVVYXVau19UU5JSeH+++/vbUtH\nR8dxf4vz5s3zcktO3Jbly5fz5JNPEhAQgM1m44EHHiA0NHRYPi+PPfYYjz32GFOmTOHiiy/uve13\nvvMd/vjHP1JaWsoPf/jD3jdyvjTN8kii46Pv0fHRd+j4qOOjN9oyGMdHnw9rIiIiIiIi5yKf7gYp\nIiIiIiJyrlJYExERERER8UEKayIiIiIiIj5IYU1ERERERMQHKayJiIiIiIj4IIU1ERERERERH6Sw\nJiIiIiIi4oMU1kRERERERHzQ/wctCrC08ooCUgAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7ff5e3e16cc0>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "metadata": {
        "id": "Iz3G5eaTS04m",
        "colab_type": "code",
        "outputId": "9d3a0b4e-59d3-4fcd-860f-f0a062c4e8e9",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "cell_type": "code",
      "source": [
        "# Test performance\n",
        "trainer.run_test_loop()\n",
        "print(\"Test loss: {0:.2f}\".format(trainer.train_state['test_loss']))\n",
        "print(\"Test Accuracy: {0:.1f}%\".format(trainer.train_state['test_acc']))"
      ],
      "execution_count": 41,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Test loss: 0.94\n",
            "Test Accuracy: 67.7%\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "kqMzljfpS09F",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Save all results\n",
        "trainer.save_train_state()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "1fMNOVJUYvhs",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "~66% test performance for our Cifar10 dataset is not bad but we can do way better."
      ]
    },
    {
      "metadata": {
        "id": "P9DcE8tHYvfX",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "# Transfer learning"
      ]
    },
    {
      "metadata": {
        "id": "EclYytw6Swh-",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "In this section, we're going to use a pretrained model that performs very well on a different dataset. We're going to take the architecture and the initial convolutional weights from the model to use on our data. We will freeze the initial convolutional weights and fine tune the later convolutional and fully-connected layers. \n",
        "\n",
        "Transfer learning works here because the initial convolution layers act as excellent feature extractors for common spatial features that are shared across images regardless of their class. We're going to leverage these large, pretrained models' feature extractors for our own dataset."
      ]
    },
    {
      "metadata": {
        "id": "mxl4PEfqTMwm",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "from torchvision import models"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "GjufXPDJTB7W",
        "colab_type": "code",
        "outputId": "709a1c26-7c67-420f-83be-bdf5409cf4cc",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 54
        }
      },
      "cell_type": "code",
      "source": [
        "model_names = sorted(name for name in models.__dict__\n",
        "    if name.islower() and not name.startswith(\"__\")\n",
        "    and callable(models.__dict__[name]))\n",
        "print (model_names)"
      ],
      "execution_count": 44,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "['alexnet', 'densenet121', 'densenet161', 'densenet169', 'densenet201', 'inception_v3', 'resnet101', 'resnet152', 'resnet18', 'resnet34', 'resnet50', 'squeezenet1_0', 'squeezenet1_1', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn']\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "daJN4BSWS016",
        "colab_type": "code",
        "outputId": "0608949f-b8ec-471e-c32b-04079b8980b9",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1173
        }
      },
      "cell_type": "code",
      "source": [
        "model_name = 'vgg19_bn'\n",
        "vgg_19bn = models.__dict__[model_name](pretrained=True) # Set false to train from scratch\n",
        "print (vgg_19bn.named_parameters)"
      ],
      "execution_count": 45,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Downloading: \"https://download.pytorch.org/models/vgg19_bn-c79401a0.pth\" to /root/.torch/models/vgg19_bn-c79401a0.pth\n",
            "100%|██████████| 574769405/574769405 [00:29<00:00, 19305160.82it/s]\n"
          ],
          "name": "stderr"
        },
        {
          "output_type": "stream",
          "text": [
            "<bound method Module.named_parameters of VGG(\n",
            "  (features): Sequential(\n",
            "    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (2): ReLU(inplace)\n",
            "    (3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (5): ReLU(inplace)\n",
            "    (6): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (7): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (8): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (9): ReLU(inplace)\n",
            "    (10): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (11): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (12): ReLU(inplace)\n",
            "    (13): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (14): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (15): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (16): ReLU(inplace)\n",
            "    (17): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (18): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (19): ReLU(inplace)\n",
            "    (20): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (21): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (22): ReLU(inplace)\n",
            "    (23): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (24): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (25): ReLU(inplace)\n",
            "    (26): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (27): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (28): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (29): ReLU(inplace)\n",
            "    (30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (31): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (32): ReLU(inplace)\n",
            "    (33): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (34): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (35): ReLU(inplace)\n",
            "    (36): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (37): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (38): ReLU(inplace)\n",
            "    (39): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (40): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (41): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (42): ReLU(inplace)\n",
            "    (43): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (44): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (45): ReLU(inplace)\n",
            "    (46): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (47): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (48): ReLU(inplace)\n",
            "    (49): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (50): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (51): ReLU(inplace)\n",
            "    (52): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "  )\n",
            "  (classifier): Sequential(\n",
            "    (0): Linear(in_features=25088, out_features=4096, bias=True)\n",
            "    (1): ReLU(inplace)\n",
            "    (2): Dropout(p=0.5)\n",
            "    (3): Linear(in_features=4096, out_features=4096, bias=True)\n",
            "    (4): ReLU(inplace)\n",
            "    (5): Dropout(p=0.5)\n",
            "    (6): Linear(in_features=4096, out_features=1000, bias=True)\n",
            "  )\n",
            ")>\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "XBudDGFz1j87",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "The VGG model we chose has a `features` and a `classifier` component. The `features` component is composed of convolution and pooling layers which act as feature extractors. The `classifier` component is composed on fully connected layers. We're going to freeze most of the `feature` component and design our own FC layers for our CIFAR10 task. You can access the default code for all models at `/usr/local/lib/python3.6/dist-packages/torchvision/models` if you prefer cloning and modifying that instead."
      ]
    },
    {
      "metadata": {
        "id": "YmzQIXsd59Rj",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class ImageModel(nn.Module):\n",
        "    def __init__(self, feature_extractor, num_hidden_units, \n",
        "                 num_classes, dropout_p):\n",
        "        super(ImageModel, self).__init__()\n",
        "        \n",
        "        # Pretrained feature extractor\n",
        "        self.feature_extractor = feature_extractor\n",
        "        \n",
        "        # FC weights\n",
        "        self.classifier = nn.Sequential(\n",
        "            nn.Linear(512, 250, bias=True),\n",
        "            nn.ReLU(),\n",
        "            nn.Dropout(0.5),\n",
        "            nn.Linear(250, 100, bias=True),\n",
        "            nn.ReLU(),\n",
        "            nn.Dropout(0.5),\n",
        "            nn.Linear(100, 10, bias=True),\n",
        "            )\n",
        "\n",
        "    def forward(self, x, apply_softmax=False):\n",
        "          \n",
        "        # Feature extractor\n",
        "        z = self.feature_extractor(x)\n",
        "        z = z.view(x.size(0), -1)\n",
        "        \n",
        "        # FC\n",
        "        y_pred = self.classifier(z)\n",
        "\n",
        "        if apply_softmax:\n",
        "            y_pred = F.softmax(y_pred, dim=1)\n",
        "        return y_pred "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "czo1bGBwXKNj",
        "colab_type": "code",
        "outputId": "9d407e14-2415-41ba-9f20-37e33f8ab716",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1139
        }
      },
      "cell_type": "code",
      "source": [
        "# Initialization\n",
        "dataset = ImageDataset.load_dataset_and_make_vectorizer(split_df)\n",
        "dataset.save_vectorizer(args.vectorizer_file)\n",
        "vectorizer = dataset.vectorizer\n",
        "model = ImageModel(feature_extractor=vgg_19bn.features, \n",
        "                   num_hidden_units=args.hidden_dim,\n",
        "                   num_classes=len(vectorizer.category_vocab), \n",
        "                   dropout_p=args.dropout_p)\n",
        "print (model.named_parameters)"
      ],
      "execution_count": 47,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "<bound method Module.named_parameters of ImageModel(\n",
            "  (feature_extractor): Sequential(\n",
            "    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (2): ReLU(inplace)\n",
            "    (3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (5): ReLU(inplace)\n",
            "    (6): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (7): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (8): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (9): ReLU(inplace)\n",
            "    (10): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (11): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (12): ReLU(inplace)\n",
            "    (13): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (14): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (15): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (16): ReLU(inplace)\n",
            "    (17): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (18): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (19): ReLU(inplace)\n",
            "    (20): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (21): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (22): ReLU(inplace)\n",
            "    (23): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (24): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (25): ReLU(inplace)\n",
            "    (26): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (27): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (28): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (29): ReLU(inplace)\n",
            "    (30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (31): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (32): ReLU(inplace)\n",
            "    (33): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (34): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (35): ReLU(inplace)\n",
            "    (36): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (37): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (38): ReLU(inplace)\n",
            "    (39): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "    (40): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (41): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (42): ReLU(inplace)\n",
            "    (43): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (44): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (45): ReLU(inplace)\n",
            "    (46): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (47): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (48): ReLU(inplace)\n",
            "    (49): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
            "    (50): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
            "    (51): ReLU(inplace)\n",
            "    (52): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
            "  )\n",
            "  (classifier): Sequential(\n",
            "    (0): Linear(in_features=512, out_features=250, bias=True)\n",
            "    (1): ReLU()\n",
            "    (2): Dropout(p=0.5)\n",
            "    (3): Linear(in_features=250, out_features=100, bias=True)\n",
            "    (4): ReLU()\n",
            "    (5): Dropout(p=0.5)\n",
            "    (6): Linear(in_features=100, out_features=10, bias=True)\n",
            "  )\n",
            ")>\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "hZybxGHoDTwQ",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Finetune last few conv layers and FC layers\n",
        "for i, param in enumerate(model.feature_extractor.parameters()):\n",
        "    if i < 36:\n",
        "        param.requires_grad = False\n",
        "    else:\n",
        "        param.requires_grad = True"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "GTbYKussTvB2",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Train\n",
        "trainer = Trainer(dataset=dataset, model=model, \n",
        "                  model_state_file=args.model_state_file, \n",
        "                  save_dir=args.save_dir, device=args.device,\n",
        "                  shuffle=args.shuffle, num_epochs=args.num_epochs, \n",
        "                  batch_size=args.batch_size, learning_rate=args.learning_rate, \n",
        "                  early_stopping_criteria=args.early_stopping_criteria)\n",
        "trainer.run_train_loop()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "NCLCnQgATvMj",
        "colab_type": "code",
        "outputId": "20bca437-868c-41c3-c95c-4b328c9024fc",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 335
        }
      },
      "cell_type": "code",
      "source": [
        "# Plot performance\n",
        "trainer.plot_performance()"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAA2gAAAE+CAYAAAD4XjP+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3Xl4lOWh/vHvzGRmsu+ZEFYhIEkg\nISBBBRRFEQTcECEqllO0PVqXVpHCL+2p1lqXU1BRK2pLdxRcgqhYtXjAilJZQkIgCausYpLJvs/6\n+yM0SlmVTCaT3J/r8jLzzrzv3DNsc8/7vM9j8Hq9XkRERERERMTvjP4OICIiIiIiIq1U0ERERERE\nRDoJFTQREREREZFOQgVNRERERESkk1BBExERERER6SRU0ERERERERDoJFTSR72jw4MF89dVX/o4h\nIiLSIbKzs7n22mv9HUOky1NBExEREZHT2rVrFxEREfTs2ZOtW7f6O45Il6aCJtLOWlpa+MUvfsHE\niRO5+uqreeKJJ3C73QD87W9/4+qrr2bSpElMnz6d3bt3n3a7iIhIZ7By5UomTZrE1KlTeeutt9q2\nv/XWW0ycOJGJEycyb948HA7HKbd//vnnTJgwoW3fb95+7rnn+PnPf8706dP505/+hMfj4Ze//CUT\nJ05k/PjxzJs3D6fTCUBlZSV33nknV1xxBddccw3r169n3bp1TJ069bjM06ZNY82aNb5+a0TaXZC/\nA4h0NX/+85/56quvWL16NS6Xi1mzZvHuu+9yxRVXsHjxYtauXUt4eDh///vfWbduHUlJSSfdPmjQ\nIH+/FBEREdxuN//4xz+4++67MZlMLFq0CIfDQVlZGU8++SRvvfUWNpuNe++9l7/85S9MmjTppNvT\n09NP+zwff/wxq1atIjY2lg8++IDNmzfz7rvv4vF4uOGGG3jvvfe47rrrWLRoEcnJybz44osUFRXx\n/e9/n08++YTy8nJKSkpISUnhyy+/5ODBg1x66aUd9C6JtB8VNJF2tm7dOubMmUNQUBBBQUFcc801\nfPrpp0yePBmDwcAbb7zB1KlTufrqqwFwOp0n3S4iItIZrF+/nvT0dMLDwwEYNWoUa9eupbq6muHD\nh5OYmAjAokWLMJlMvPnmmyfdvmXLltM+z7Bhw4iNjQVg4sSJXH755ZjNZgDS09M5dOgQ0Frkfve7\n3wGQlpbGRx99hMViYeLEiaxevZqUlBTWrFnDFVdcgcViaf83RMTHNMRRpJ1VVlYSFRXVdjsqKoqK\nigrMZjN/+tOfyMvLY+LEidxyyy3s3LnzlNtFREQ6g9zcXNatW8fIkSMZOXIkH374IStXrqSqqorI\nyMi2x1mtVoKCgk65/Uy++W9nZWUl8+fPZ+LEiUyaNImPPvoIr9cLQHV1NREREW2P/XdxnDJlCqtX\nrwZgzZo1TJ48+dxeuIifqKCJtLP4+Hiqq6vbbldXVxMfHw+0ftP37LPPsmHDBsaOHctDDz102u0i\nIiL+VFNTw8aNG/n888/ZvHkzmzdvZtOmTRQWFmI0Gqmqqmp7bH19PXa7nZiYmJNuN5lMbddkA9TW\n1p7yeZ9++mmCgoJ45513eP/99xk3blzbfdHR0ccd//DhwzidTrKysnC5XKxdu5bdu3czevTo9nob\nRDqUCppIO7vssst44403cLvdNDY2smrVKsaNG8fOnTu57777cDgcWCwWhg4disFgOOV2ERERf1u9\nejUXXXTRcUMFg4KCGDt2LA6Hg7y8PA4fPozX6+Whhx7ijTfeYNy4cSfdnpCQQHl5ORUVFbjdbt55\n551TPm9FRQXnn38+FouFkpIStm7dSmNjIwDjx49n5cqVAOzZs4dp06bhdrsxGo1MnjyZX/3qV4wf\nP75teKRIoNE1aCLn4LbbbsNkMrXdfvTRR7nttts4dOgQU6ZMwWAwMGnSpLbrynr37s3UqVMxm82E\nhYXxi1/8gvPPP/+k20VERPztrbfeYvbs2SdsnzBhAi+88AKPPPIIs2fPxmQykZ6ezve//32sVusp\nt994441cf/319OzZk+uuu47i4uKTPu+cOXOYP38+ubm5jBw5kvnz5/Ozn/2MjIwM5s2bx/z58xk/\nfjxhYWEsXLiQ4OBgoHWY4x//+EcNb5SAZvD+e0CviIiIiEgAs9vt3HDDDaxbt+64L1BFAomGOIqI\niIhIl/Dss89y8803q5xJQFNBExEREZGAZrfbueKKK7Db7cyZM8ffcUTOiYY4ioiIiIiIdBI6gyYi\nIiIiItJJqKCJiIiIiIh0Eh0+zX55ed05HyMmJpSqqsZ2SNMxAimvsvpGIGWFwMqrrL7RXlkTEiLa\nIU330d3+jQykrBBYeZXVN5TVdwIpb3tkPd2/jwF5Bi0oKLBm5gmkvMrqG4GUFQIrr7L6RiBlleMF\n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\nwPjx49m6dWur+bZu3crEiRMBGDlyJF9//XWXZxUREeejgiYiItJOGRkZWK1WTCYTvr6+LY8HBQVR\nXFzcat6SkhICAwMBMBqNGAwG6urqujSviIg4Hw1xFBERaafVq1czZcqU8x632WxtLtueeQICPHFx\nMV1Wtu+zWHyu+DW6ijNlBefKq6ydQ1k7jzPl7cysKmgiIiLttG3bNp599lkMBgPl5eUtjxcWFhIS\nEtJq3pCQEIqLixk0aBD19fXYbDbM5ovfk6ysrOqKMzrT+YPOlBWcK6+ydg5l7TzOlLcjsl6s4GmI\no4iISDsUFhbi5eWF2WzG1dWVqKgodu7cCcAnn3zC6NGjW82fkpLCRx99BMCGDRsYNmxYl2cWERHn\no4ImIiLSDsXFxS3nlAHMnTuXhQsXMn36dMLDwxk5ciQAs2bNAuDmm2+mqamJGTNmsHTpUmbPnm2X\n3CIi4lw0xFFERKQd4uPjeeONN1qm+/fvz7///e/z5luyZAlAy73PRERELoWOoImIiIiIiDgIFTQR\nEREREREHoYImIiIiIiLiIAy29tyYRURERERERDqdjqCJiIiIiIg4CBU0ERERERERB6GCJiIiIiIi\n4iBU0ERERERERByECpqIiIiIiIiDUEETERERERFxEC72DnAhL7zwAnv27MFgMDB37lwSExNbntuy\nZQsLFy7EZDIxZswYHnrooTaXsVfWr776ioULF2I0GomMjOT5559nx44dPPbYY8TExAAwYMAAfvvb\n33ZJ1rbyTpgwgbCwMEwmEwDz588nNDTU4d7bwsJCnnrqqZb5jh8/zuzZs6mvr2fRokWEh4cDMHLk\nSGbNmtUlWbOzs3nwwQf5+c9/zt13393qOUdbZ9vK62jr7cWyOto6+2NZHXGd/eMf/8iuXbtoaGjg\n17/+NTfccEPLc464zsp3tI3s+qyO9lnjTNtHcK5tpLaPXZ/XEddbu28jbQ5m27Zttl/96lc2m81m\ny83NtU2dOrXV8zfddJPt1KlTtsbGRtuMGTNsOTk5bS5jr6wTJ0605efn22w2m+2RRx6xbdy40fbV\nV1/ZHnnkkS7J90Nt5R0/frytsrLykpaxV9Zv1dfX26ZPn26rrKy0vfXWW7aXXnqpS/J937lz52x3\n33237dlnn7W9+eab5z3vSOtse/I60nrbVlZHWmfbyvotR1hnt27darv//vttNpvNVlpaahs7dmyr\n5x1tnZXvaBtpn6yO9FnjTNtHm825tpHaPnYebSMvjcMNcdy6dSvXX389ANHR0Zw5c4bKykqguVH7\n+flhtVoxGo2MHTuWrVu3XnQZe2UFWLNmDWFhYQAEBgZSVlbW6Zku5nLeJ0d9b7/19ttvc+ONN+Ll\n5dXpmX6M2Wzm9ddfJyQk5LznHG2dbSsvONZ621bWC7HXe9verI6wzl5zzTUsWrQIAF9fX6qrq2ls\nbAQcc52V72gb2Tm0few8zrSN1Pax82gbeWkcrqCVlJQQEBDQMh0YGEhxcTEAxcXFBAYGnvfcxZax\nV1YAb29vAIqKiti8eTNjx44FIDc3lwceeIAZM2awefPmTs/Z3rwA8+bNY8aMGcyfPx+bzeaw7+23\nVq1axZ133tkyvX37dmbOnMm9997Lvn37Oj0ngIuLC+7u7hd8ztHWWbh4XnCs9batrOA462x7soJj\nrLMmkwlPT08AVq9ezZgxY1qGwTjiOivf0TbSPlnBcT5rnGn7CM61jdT2sfNoG3lpHPIctO+z2Wxd\nskxHuNDPPX36NA888ADz5s0jICCAiIgIHn74YW666SaOHz/OPffcwyeffILZbLZ73kcffZTRo0fj\n5+fHQw89xMcff9zmMl3lQj939+7dREVFtXxgJiUlERgYyLhx49i9ezfPPPMM7733XldHvSz2el9/\njCOvt9/nyOvshTjaOrt+/XpWr17N//zP/1zyso70vvZk2kZ2TVZH/qzp7ttHcKzPG0ddZ3/IkdfZ\nH+No6609t5EOV9BCQkIoKSlpmS4qKsJisVzwucLCQkJCQnB1df3RZeyVFaCyspJf/vKXPP7444wa\nNQqA0NBQbr75ZgDCw8MJDg6msLCQvn372j3v5MmTW74fM2YM2dnZbS5jr6wAGzduZMSIES3T0dHR\nREdHAzBkyBBKS0tpbGxs+auHPTjaOtsejrbeXowjrbPt4Ujr7Jdffslrr73GG2+8gY+PT8vjzrjO\n9iTaRtonqyN91nSX7SM43jrbFkdaZ9viSOtseznSemvvbaTDDXFMSUlpaflZWVmEhIS0NOk+ffpQ\nWVnJiRMnaGhoYMOGDaSkpFx0GXtlBXjppZe49957GTNmTMtja9eu5e9//zvQfJj09OnThIaGdnrW\ntvJWVFQwc+ZM6urqANixYwcxMTEO+94CZGZmMmjQoJbp119/nffffx9ovlJQYGCg3Tc+jrbOtoej\nrbc/xtHW2fZwlHW2oqKCP/7xj/z1r3/F39+/1XPOuM72JNpGdn1WR/us6S7bR3C8dbYtjrTOXoyj\nrbPt5SjrrSNsIw02Rzu+SfOlQHfu3InBYGDevHns27cPHx8fJk6cyI4dO5g/fz4AN9xwAzNnzrzg\nMt//B7ZH1lGjRnHNNdcwZMiQlnknTZrELbfcwlNPPcXZs2epr6/n4YcfbhnDbM+8EydO5F//+hfv\nvPMObm5uxMbG8tvf/haDweBw7+3EiRMBuPXWW/nHP/5BcHAwAAUFBTz99NPYbDYaGhq67PKxe/fu\n5Q9/+AMnT57ExcWF0NBQJkyYQJ8+fRxynb1YXkdbb9t6bx1pnW0rKzjOOrtixQoWL15MZGRky2PD\nhg1j4MCBDrnOSmvaRnZtVkf7rGkrKzjOZw041zZS20f75QXHWW8dYRvpkAVNRERERESkJ3K4IY4i\nIiIiIiI9lQqaiIiIiIiIg1BBExERERERcRAqaCIiIiIiIg5CBU1ERERERMRBqKCJiIiIiIg4CBU0\nERERERERB6GCJiIiIiIi4iD+H4+u0AEVg8YvAAAAAElFTkSuQmCC\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f95d15a9048>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "metadata": {
        "id": "Hjn0HJVoTvJ0",
        "colab_type": "code",
        "outputId": "789735eb-61fc-4745-e133-c161f9d6fec9",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "cell_type": "code",
      "source": [
        "# Test performance\n",
        "trainer.run_test_loop()\n",
        "print(\"Test loss: {0:.2f}\".format(trainer.train_state['test_loss']))\n",
        "print(\"Test Accuracy: {0:.1f}%\".format(trainer.train_state['test_acc']))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Test loss: 0.53\n",
            "Test Accuracy: 84.5%\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "metadata": {
        "id": "ZQVrGTNNTvH0",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Save all results\n",
        "trainer.save_train_state()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "7CL689FebJhf",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "Much better performance! If you let it train long enough, we'll actually reah ~95% accuracy :)"
      ]
    },
    {
      "metadata": {
        "id": "02iDXCtiYo5K",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "## Inference"
      ]
    },
    {
      "metadata": {
        "id": "cVT--tAvnOu7",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "from pylab import rcParams\n",
        "rcParams['figure.figsize'] = 1, 1"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "1qQjnXpnYoMM",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "class Inference(object):\n",
        "    def __init__(self, model, vectorizer):\n",
        "        self.model = model\n",
        "        self.model.to(\"cpu\")\n",
        "        self.vectorizer = vectorizer\n",
        "  \n",
        "    def predict_category(self, image):\n",
        "        # Vectorize\n",
        "        image_vector = self.vectorizer.vectorize(image)\n",
        "        image_vector = torch.tensor(image_vector).unsqueeze(0)\n",
        "        \n",
        "        # Forward pass\n",
        "        self.model.eval()\n",
        "        y_pred = self.model(x=image_vector, apply_softmax=True)\n",
        "\n",
        "        # Top category\n",
        "        y_prob, indices = y_pred.max(dim=1)\n",
        "        index = indices.item()\n",
        "\n",
        "        # Predicted category\n",
        "        category = vectorizer.category_vocab.lookup_index(index)\n",
        "        probability = y_prob.item()\n",
        "        return {'category': category, 'probability': probability}\n",
        "    \n",
        "    def predict_top_k(self, image, k):\n",
        "        # Vectorize\n",
        "        image_vector = self.vectorizer.vectorize(image)\n",
        "        image_vector = torch.tensor(image_vector).unsqueeze(0)\n",
        "        \n",
        "        # Forward pass\n",
        "        self.model.eval()\n",
        "        y_pred = self.model(x=image_vector, apply_softmax=True)\n",
        "        \n",
        "        # Top k categories\n",
        "        y_prob, indices = torch.topk(y_pred, k=k)\n",
        "        probabilities = y_prob.detach().numpy()[0]\n",
        "        indices = indices.detach().numpy()[0]\n",
        "\n",
        "        # Results\n",
        "        results = []\n",
        "        for probability, index in zip(probabilities, indices):\n",
        "            category = self.vectorizer.category_vocab.lookup_index(index)\n",
        "            results.append({'category': category, 'probability': probability})\n",
        "\n",
        "        return results"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "MbTRzW8CYoWc",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Get a sample\n",
        "sample = split_df[split_df.split==\"test\"].iloc[1000]"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "DswQ0pikYoR_",
        "colab_type": "code",
        "outputId": "dedeef0c-be8e-4015-b401-7bb57080a641",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 139
        }
      },
      "cell_type": "code",
      "source": [
        "# Inference\n",
        "inference = Inference(model=model, vectorizer=vectorizer)\n",
        "prediction = inference.predict_category(sample.image)\n",
        "print (\"Actual:\", sample.category)\n",
        "plt.imshow(sample.image)\n",
        "plt.axis(\"off\")\n",
        "print(\"({} → p={:0.2f})\".format(prediction['category'], \n",
        "                                prediction['probability']))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Actual: car\n",
            "(car → p=1.00)\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f95cae77588>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "metadata": {
        "id": "72_-iRQxYoQK",
        "colab_type": "code",
        "outputId": "7a65b46b-fc51-4b24-eba1-d2a404af6e91",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 292
        }
      },
      "cell_type": "code",
      "source": [
        "# # Top-k inference\n",
        "top_k = inference.predict_top_k(sample.image, k=len(vectorizer.category_vocab))\n",
        "print (\"Actual:\", sample.category)\n",
        "plt.imshow(sample.image)\n",
        "plt.axis(\"off\")\n",
        "for result in top_k:\n",
        "    print (\"{} → (p={:0.2f})\".format(result['category'], \n",
        "                                     result['probability']))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Actual: car\n",
            "car → (p=1.00)\n",
            "ship → (p=0.00)\n",
            "truck → (p=0.00)\n",
            "plane → (p=0.00)\n",
            "cat → (p=0.00)\n",
            "bird → (p=0.00)\n",
            "frog → (p=0.00)\n",
            "deer → (p=0.00)\n",
            "horse → (p=0.00)\n",
            "dog → (p=0.00)\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f95d139ad68>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "metadata": {
        "id": "1YHneO3SStOp",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "# TODO"
      ]
    },
    {
      "metadata": {
        "id": "gGHaKTe1SuEk",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "- segmentation\n",
        "- interpretability via activation maps\n",
        "- processing images of different sizes\n",
        "- save split_dataframe (wiht numpy image arrays) to csv and reload dataframe from csv during inference"
      ]
    }
  ]
}